﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Sofire.DataComm.Remoting.Server.Settings;
using System.Threading;

namespace Sofire.DataComm.Remoting.Server
{
    /// <summary>
    /// 远程应用程序管理器。
    /// </summary>
    public static class RemotingApplication
    {
        private static List<_IServer> _Servers;

        /// <summary>
        /// 应用程序的状态发生改变后发生。
        /// </summary>
        public static event CommunicationStateEventHandler ApplicationStateChanged;
        /// <summary>
        /// 服务的状态发生改变后发生。
        /// </summary>
        public static event CommunicationStateEventHandler ServerStateChanged;


        /// <summary>
        /// 客户成功连接后发生。
        /// </summary>
        public static event RemotingContextEventHandler ClientJoined;
        /// <summary>
        /// 客户连接过程中抛出异常后发生。
        /// </summary>
        public static event ClientFailedEventHandler ClientFailed;
        /// <summary>
        /// 客户远程交互时发生。
        /// </summary>
        public static event EventHandler ClientInvoking;
        /// <summary>
        /// 客户远程交互后发生。
        /// </summary>
        public static event ClientInvokedEventHandler ClientInvoked;
        /// <summary>
        /// 客户断开连接后发生。
        /// </summary>
        public static event RemotingContextEventHandler ClientQuitted;

        private static ApplicationSetting _Setting;
        /// <summary>
        /// 获取当前应用程序配置。
        /// </summary>
        public static ApplicationSetting Setting
        {
            get
            {
                return _Setting;
            }
        }


        /// <summary>
        /// 指示配置文件路径，初始化交互应用程序。
        /// </summary>
        /// <param name="settingPath">应用程序配置文件路径。</param>
        public static void Initialization(string settingPath)
        {
            XmlDocument document = new XmlDocument();
            document.Load(settingPath);
            var firstNode = document.DocumentElement as XmlElement;
            if(firstNode == null || firstNode.Name != "Application") throw new SettingException(ServerErrorCode.UnfoundApplication);
            var setting = new ApplicationSetting();
            setting.Initialization(firstNode);
            Initialization(setting);

        }

        /// <summary>
        /// 指示应用程序配置，初始化交互应用程序。
        /// </summary>
        /// <param name="applicationSetting">应用程序配置。</param>
        public static void Initialization(ApplicationSetting applicationSetting)
        {
            RemotingApplication.ThorwEceptionWithIsOpen();

            _Servers = new List<_IServer>();

            var query1 = from host in applicationSetting
                         group host by host.Address;
            foreach(var hostGroups in query1)
            {
                var query2 = from host in hostGroups
                             group host by host.Port;
                foreach(var hostsGroup2 in query2)
                {
                    foreach(var host in hostsGroup2)
                    {
                        //Dictionary<string, ServiceSetting> services = new Dictionary<string, ServiceSetting>(host.Count);
                        //foreach(var service in host)
                        //{
                        //    services.Add(service.FullName, service);
                        //}

                        _IServer server = new RemotingServer(host);
                        _Servers.Add(server);
                    }
                }
            }

            _Setting = applicationSetting;

        }

        //private static SynchronizationObject _SyncObject = new SynchronizationObject();
        //public static SynchronizationObject SyncObject
        //{
        //    get
        //    {
        //        return _SyncObject;
        //    }
        //}

        #region Application Events

        private static void RaiseApplicationStateChanged(Exception ex)
        {
            var ev = RemotingApplication.ApplicationStateChanged;
            if(ev != null)
            {
                ev(_Setting, new CommunicationFailedStateEventArgs(ex));
            }
        }

        private static void RaiseApplicationEvent(CommunicationState state)
        {
            var ev = RemotingApplication.ApplicationStateChanged;
            if(ev != null)
            {
                ev(_Setting, new CommunicationStateEventArgs(state));
            }
        }

        #endregion
        #region Sever Events

        internal static void RaiseServerStateChanged(IServer server, CommunicationStateEventArgs e)
        {
            var ev = RemotingApplication.ServerStateChanged;
            if(ev != null)
            {
                ev(server, e);
            }
        }

        #endregion

        #region Client Events

        internal static void RaiseClientJoined(IServer server, RemotingContext context)
        {
            if(ClientJoined != null) ClientJoined(server, new RemotingContextEventArgs(context));
        }

        internal static void RaiseClientInvoking(IServer server)
        {
            if(ClientInvoking != null) ClientInvoking(server, EventArgs.Empty);
        }

        internal static void RaiseClientInvoked(IServer server, RemotingResult result)
        {
            if(ClientInvoked != null) ClientInvoked(server, new ClientInvokedEventArgs(result));
        }

        /// <summary>
        /// 触发客户异常。
        /// </summary>
        /// <param name="server">服务器。</param>
        /// <param name="ex">异常信息。</param>
        /// <returns>返回一个值，表示是否终止客户。</returns>
        public static bool RaiseClientFailed(IServer server, Exception ex)
        {
            if(ClientFailed != null)
            {
                var e = new ClientFailedEventArgs(ex);
                ClientFailed(server, e);
                return e.TerminateConnection;
            }
            return false;
        }

        internal static void RaiseClientQuitted(IServer server, RemotingContext context)
        {
            if(ClientQuitted != null) ClientQuitted(server, new RemotingContextEventArgs(context));
        }

        #endregion

        /// <summary>
        /// 获取当前交互应用程序的启动状态。
        /// </summary>
        public static bool Actived
        {
            get
            {
                if(!_IsOpen || _Setting == null) return false;
                foreach(var server in _Servers)
                {
                    if(server.State != CommunicationState.Opened) return false;
                }
                return true;
            }
        }

        private static readonly object ContextsSyncObject = new object();
        private static Dictionary<string, RemotingContext> _Contexts;
        /// <summary>
        /// 获取所有上下文。
        /// </summary>
        public static IEnumerable<RemotingContext> Contexts
        {
            get
            {
                if(_Contexts == null || !_IsOpen) return new List<RemotingContext>(0);
                return _Contexts.Values;
            }
        }

        internal static void ActivingContext(RemotingContext context)
        {
            if(_Contexts == null || !_IsOpen) return;
            lock(ContextsSyncObject)
            {
                _Contexts[context.ClientEndPoint] = context;
            }
        }

        internal static void InactivingContext(RemotingContext context)
        {
            if(_Contexts == null || !_IsOpen) return;
            lock(ContextsSyncObject)
            {
                _Contexts.Remove(context.ClientEndPoint);
            }
        }

        internal static RemotingContext FindContext(string address)
        {
            RemotingContext context;
            lock(ContextsSyncObject) _Contexts.TryGetValue(address, out context);
            return context;
        }

        private static bool _IsOpen;

        internal static void ThorwEceptionWithIsOpen()
        {
            if(_IsOpen) throw new Exception("无法在应用程序启动时添加主机。");
        }

        /// <summary>
        /// 启动远程应用程序。
        /// </summary>
        /// <returns>返回应用程序的启动结果。</returns>
        public static Result Open()
        {
            if(_Setting == null) return "远程配置尚未初始化！";
            if(_IsOpen) return "应用程序已启动。";

            RaiseApplicationEvent(CommunicationState.Opening);

            try
            {
                _Contexts = new Dictionary<string, RemotingContext>(11, StringComparer.OrdinalIgnoreCase);
                foreach(var server in _Servers)
                {
                    server.Open();
                }
                _IsOpen = true;
                return Result.Successfully;
            }
            catch(Exception e)
            {
                RaiseApplicationStateChanged(e);
                Close();
                return e;
            }
            finally
            {
                if(_IsOpen) RaiseApplicationEvent(CommunicationState.Opened);
            }
        }

        /// <summary>
        /// 关闭远程应用程序。
        /// </summary>
        public static void Close()
        {
            if(_Setting == null) return;
            _Contexts = null;
            _IsOpen = false;


            RaiseApplicationEvent(CommunicationState.Closing);

            foreach(var server in _Servers)
            {
                try { server.Close(); }
                catch(Exception ex)
                {
                    RaiseServerStateChanged(server, new CommunicationFailedStateEventArgs(ex));
                }
            }
            ServiceSetting.ResetGlobalInstances();

            RaiseApplicationEvent(CommunicationState.Closed);

        }
    }
}
